#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/time.h>
#include <errno.h>
#include "../md5/md5.c"
#include "../DES/des.c"
#include "MsgType.h"

// Step1 发送用户名到AS
void SendToAS(char *clientID);
// Step2 从AS接收消息A和消息B
void ReceiveFromAS(MsgA *msgA, MsgB *msgB);
// Step3 解密消息A得到K_CT会话密钥
char* DecryptMsgA(MsgA msgA, char *userPassword);
void CreatMsgC(MsgC *msgC, char *serviceID, MsgB msgB);
void CreatMsgD(MsgD *msgD, char *K_CT, char *clientID);
// Step4 发送消息C和D到TGS
void SendToTGS(MsgC *msgC, MsgD *msgD);
// Step5 从TGS接收消息
void ReceiveFromTGS(MsgE *msgE, MsgF *msgF);
// Step6 解密消息F得到K_CS
char* DecryptMsgF(MsgF msgF, char *K_CT);
void CreateMsgG(MsgG *msgG, char *username, char *K_CS);
// Step7 发送消息E和G到SS
void SendToSS(MsgE msgE, MsgG msgG);
// Step8 从SS接收消息
void ReceiveFromSS(MsgH *msgH);
// Step9 认证
void AuthenticateSS(char *oldTs, char *newTs);


int main()
{
    char username[BUFSIZ];// = "Bob";
    char password[BUFSIZ];// = "654321";
    char serviceID[BUFSIZ];// = "10";
    MsgA msgA;
    MsgB msgB;
    MsgC msgC;
    MsgD msgD;
    MsgE msgE;
    MsgF msgF;  
    MsgG msgG;  
    MsgH msgH;
    MsgAInit(&msgA);
    MsgBInit(&msgB);
    MsgCInit(&msgC);
    MsgDInit(&msgD);
    MsgEInit(&msgE);
    MsgFInit(&msgF);
    MsgGInit(&msgG);    
    MsgHInit(&msgH);    

    printf("输入用户名: ");
    fgets(username, BUFSIZ, stdin);
    int len = strlen(username);
    username[len - 1] = '\0';

    printf("输入口令: ");
    fgets(password, BUFSIZ, stdin);
    len = strlen(password);
    password[len - 1] = '\0';

    // msgH.ts = "Empty";
    // while (1)
    // {
    //     ReceiveFromSS(&msgH);
    //     if (msgH.ts != "Empty")
    //     {
    //         printf("接收消息H\n\n");
    //         break;
    //     }
    // }

    SendToAS(username);
    msgB.K_CT = "Empty";
    while (1)
    {
        ReceiveFromAS(&msgA, &msgB);
        if (msgB.K_CT != "Empty")
        {
            printf("......\n接收消息A和消息B\n");
            break;
        }
    }
    char *K_CT = DecryptMsgA(msgA, password);

    printf("\n输入服务ID: ");
    fgets(serviceID, BUFSIZ, stdin);
    len = strlen(serviceID);
    serviceID[len - 1] = '\0';
    
    CreatMsgC(&msgC, serviceID, msgB);
    CreatMsgD(&msgD, K_CT, username);
    SendToTGS(&msgC, &msgD);

    msgF.K_CS = "Empty";
    while (1)
    {
        ReceiveFromTGS(&msgE, &msgF);
        if (msgF.K_CS != "Empty")
        {
            printf("......\n接收消息E和消息F\n");
            break;
        }
    }

    char *K_CS = DecryptMsgF(msgF, K_CT);
    CreateMsgG(&msgG, username, K_CS);
    SendToSS(msgE, msgG);          

    msgH.ts = "Empty";
    while (1)
    {
        ReceiveFromSS(&msgH);
        if (msgH.ts != "Empty")
        {
            printf("......\n接收消息H\n");
            break;
        }
    }

    char *oldTs = DecryptionBS(msgG.timestamp, K_CS);
    char *newTs = DecryptionBS(msgH.ts, K_CS);
    AuthenticateSS(oldTs, newTs);

    exit(EXIT_SUCCESS);
}

// Step1 发送用户名到AS
void SendToAS(char *clientID)
{
    struct msg_st data;
    //建立消息队列
    int msgid = msgget((key_t)111, 0666 | IPC_CREAT);
    if (msgid == -1)
    {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }
    data.msg_type = 1;
    strcpy(data.text, clientID);
    // 发送消息
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }
    printf("......\n用户%s请求服务\n", clientID);
}

// Step2 从AS接收消息A和消息B
void ReceiveFromAS(MsgA *msgA, MsgB *msgB)
{
    struct msg_st data;
    long int msgtype = 0;
    //建立消息队列
    int msgid = msgget((key_t)112, 0666 | IPC_CREAT);
    if (msgid == -1)
    {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }

    // 接收消息A
    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgA->K_CT, data.text);
    
    // 接收消息B（ID、地址、有效期、TGS）
    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgB->clientID, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgB->clientAddr, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgB->vaildity, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    msgB->K_CT = (char *)malloc(BUFSIZ*sizeof(char));    
    strcpy(msgB->K_CT, data.text);

    // 删除消息队列
    if (msgctl(msgid, IPC_RMID, 0) == -1)
    {
        perror("msgclt failed");
        exit(EXIT_FAILURE);
    }
}

// Step3 解密消息A得到K_CT会话密钥
char* DecryptMsgA(MsgA msgA, char *userPassword){
    char *DeK_CT;
    DeK_CT = (char *)malloc(BUFSIZ*sizeof(char));    
    unsigned char *hashPassword = MD5Encryption(userPassword);
    DeK_CT = DecryptionBS(msgA.K_CT, hashPassword);
    return DeK_CT;
}

void CreatMsgC(MsgC *msgC, char *serviceID, MsgB msgB){
    strcpy(msgC->serviceID, serviceID);
    msgC->msgB = msgB;
}

void CreatMsgD(MsgD *msgD, char *K_CT, char *clientID){
    msgD->clientID = EncryptionBS(clientID, K_CT);
    // 获得时间戳
    struct timeval tv;
    char timestamp[10];
    gettimeofday(&tv, NULL);
    sprintf(timestamp, "%ld", tv.tv_sec);
    msgD->timestamp = EncryptionBS(timestamp, K_CT);
}

// Step4 发送消息C和D到TGS
void SendToTGS(MsgC *msgC, MsgD *msgD){
    struct msg_st data;
    //建立消息队列
    int msgid = msgget((key_t)113, 0666 | IPC_CREAT);
    if (msgid == -1)
    {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }
    // 发送消息C
    data.msg_type = 1;
    strcpy(data.text, msgC->serviceID);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }

    strcpy(data.text, msgC->msgB.clientID);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }
    strcpy(data.text, msgC->msgB.clientAddr);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }
    strcpy(data.text, msgC->msgB.vaildity);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }
    strcpy(data.text, msgC->msgB.K_CT);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }

    // 发送消息D
    strcpy(data.text, msgD->clientID);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }
    strcpy(data.text, msgD->timestamp);
    if (msgsnd(msgid, (void *)&data, BUFSIZ, 0) == -1)
    {
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);
    }
    printf("......\n发送消息C和消息D到TGS\n");
}

// Step5 从TGS接收消息
void ReceiveFromTGS(MsgE *msgE, MsgF *msgF){
    struct msg_st data;
    long int msgtype = 0;
    //建立消息队列
    int msgid = msgget((key_t)114, 0666 | IPC_CREAT);
    if (msgid == -1)
    {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgE->serviceID, data.text);
    
    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgE->clientID, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgE->clientAddr, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgE->vaildity, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgE->K_CS, data.text);

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    msgF->K_CS = (char *)malloc(BUFSIZ*sizeof(char));    
    strcpy(msgF->K_CS, data.text);

    // 删除消息队列
    if (msgctl(msgid, IPC_RMID, 0) == -1)
    {
        perror("msgclt failed");
        exit(EXIT_FAILURE);
    }
}

// Step6 解密消息F得到K_CS
char* DecryptMsgF(MsgF msgF, char *K_CT){
    char *K_CS = DecryptionBS(msgF.K_CS, K_CT);
    return K_CS;
}

void CreateMsgG(MsgG *msgG, char *username, char *K_CS){
    msgG->clientID = EncryptionBS(username, K_CS);
    // 获得时间戳
    struct timeval tv;
    char timestamp[10];
    gettimeofday(&tv, NULL);
    sprintf(timestamp, "%ld", tv.tv_sec);
    msgG->timestamp = EncryptionBS(timestamp, K_CS);
}

// Step7 发送消息E和G到SS
void SendToSS(MsgE msgE, MsgG msgG){
    struct msg_st data;
    long int msgtype = 0;
    //建立消息队列
    int msgid = msgget((key_t)115, 0666 | IPC_CREAT);
    if (msgid == -1)
    {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }

    data.msg_type = 1;
    strcpy(data.text, msgE.serviceID);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }

    strcpy(data.text, msgE.clientID);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }

    strcpy(data.text, msgE.clientAddr);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }

    strcpy(data.text, msgE.vaildity);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }

    strcpy(data.text, msgE.K_CS);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }   

    strcpy(data.text, msgG.clientID);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }

    strcpy(data.text, msgG.timestamp);
    if(msgsnd(msgid, (void*)&data, BUFSIZ, 0) == -1)  
    {  
        perror("msgsnd failed\n");
        exit(EXIT_FAILURE);  
    }
    printf("......\n发送消息E和消息G到SS\n");
}

// Step8 从SS接收消息
void ReceiveFromSS(MsgH *msgH){
    struct msg_st data;
    long int msgtype = 0;
    //建立消息队列
    int msgid = msgget((key_t)116, 0666 | IPC_CREAT);
    if (msgid == -1)
    {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }

    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    strcpy(msgH->clientID, data.text);
    
    if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1)
    {
        perror("msgcrv failed");
        exit(EXIT_FAILURE);
    }
    msgH->ts = (char*)malloc(BUFSIZ * sizeof(char));
    strcpy(msgH->ts, data.text);
    
    // 删除消息队列
    if (msgctl(msgid, IPC_RMID, 0) == -1)
    {
        perror("msgclt failed");
        exit(EXIT_FAILURE);
    }
}

// Step9 认证
void AuthenticateSS(char *oldTs, char *newTs){
    int ts1 = atoi(oldTs);
    int ts2 = atoi(newTs);
    int sub = ts2 - ts1;
    if(sub == 1){
        printf("......\nSS认证成功\n");
    }else
    {
        printf("......\nSS认证失败\n");
    }
}
